#include "dynamic-obj.h"


class DynamicObjImpl
{
public:
    virtual ~DynamicObjImpl() {}

    virtual DynamicObjImpl *clone() const = 0;

    virtual void free() {
        delete this;
    }

    virtual void print(std::ostream &out) const = 0;
};

namespace
{

class NullDynamicObj : public DynamicObjImpl
{
public:
    static NullDynamicObj *alloc();

    virtual ~NullDynamicObj() {}

    virtual DynamicObjImpl *clone() const;

    virtual void free() override;

    virtual void print(std::ostream &out) const override {
        out << "nullobj";
    }

    static NullDynamicObj obj;

private:
    NullDynamicObj() {}
};

NullDynamicObj NullDynamicObj::obj;

NullDynamicObj *NullDynamicObj::alloc() {
    return &NullDynamicObj::obj;
}

DynamicObjImpl *NullDynamicObj::clone() const {
    return &NullDynamicObj::obj;
}

void NullDynamicObj::free() {
    // do nothing because NullDynamicObj::clone() never construct new object
}


class Int32DynamicObj : public DynamicObjImpl
{
public:
    static Int32DynamicObj *alloc(int32_t value) {
        return new Int32DynamicObj(value);
    }

    virtual ~Int32DynamicObj() {}

    virtual DynamicObjImpl *clone() const override {
        return alloc(value_);
    }

    virtual void print(std::ostream &out) const override {
        out << value_;
    }

private:
    Int32DynamicObj(int32_t value)
        : value_(value) {
    }

private:
    int32_t value_;
};


class StringDynamicObj : public DynamicObjImpl
{
public:
    static StringDynamicObj *alloc(const std::string_view &str) {
        return new StringDynamicObj(str);
    }

    static StringDynamicObj *alloc(std::string &&str) {
        return new StringDynamicObj(std::move(str));
    }

    virtual ~StringDynamicObj() {}

    virtual StringDynamicObj *clone() const override {
        return alloc(str_);
    }

    virtual void print(std::ostream &out) const override {
        out << str_;
    }

private:
    StringDynamicObj(const std::string_view &str)
        : str_(str) {
    }

    StringDynamicObj(std::string &&str)
        : str_(std::move(str)) {
    }

private:
    std::string str_;
};


} // namespace anonymous


DynamicObj::DynamicObj()
    : impl_(NullDynamicObj::alloc()) {
}

DynamicObj::DynamicObj(int32_t n)
    : impl_(Int32DynamicObj::alloc(n)) {
}

DynamicObj::DynamicObj(const std::string &str)
    : impl_(StringDynamicObj::alloc(str)) {
}

DynamicObj::DynamicObj(const std::string_view &str)
    : impl_(StringDynamicObj::alloc(str)) {
}

DynamicObj::DynamicObj(std::string &&str)
    : impl_(StringDynamicObj::alloc(std::move(str))) {
}

DynamicObj::~DynamicObj() {
    free();
}

DynamicObj &DynamicObj::operator=(const DynamicObj &other) {
    if (this != &other) {
        free();
    }
    impl_ = other.impl_->clone();
    return *this;
}

void DynamicObj::print(std::ostream &out) const {
    impl_->print(out);
}

void DynamicObj::free() {
    if (impl_) {
        impl_->free();
        impl_ = nullptr;
    }
}

